#include "ring_buffer.h"

/**
 * @brief 	 配置项的限制，当使用dma来存放数据的时候，必须允许数据覆盖和分割存储
 */
#define CONFIG_LIMIT(rb_cfg)                \
    do {                                    \
        if ((rb_cfg).is_dma_store) {        \
            (rb_cfg).allow_overwrite   = 1; \
            (rb_cfg).allow_sqlit_store = 1; \
        }                                   \
    } while (0)

/**
 * @brief 	 缓存区是否已满
 */
#define IS_FULL(p_rb) \
    (((p_rb)->r.flag != (p_rb)->w.flag) && ((p_rb)->r.index == (p_rb)->w.index))

/**
 * @brief 	 缓存区是否为空
 */
#define IS_EMTRY(p_rb) \
    (((p_rb)->r.flag == (p_rb)->w.flag) && ((p_rb)->r.index == (p_rb)->w.index))

/**
 * @brief 	 复制元素
 */
#define ELEM_COPY(target_elem, source_elem, elem_size)                       \
    do {                                                                     \
        for (uint32_t _i = 0; _i < (elem_size); _i++) {                      \
            ((uint8_t *)(target_elem))[_i] = ((uint8_t *)(source_elem))[_i]; \
        }                                                                    \
    } while (0)

/**
 * @brief 	 加锁
 */
#define RB_LOCK(p_rb)       \
    do {                    \
        if ((p_rb)->lock)   \
            (p_rb)->lock(); \
    } while (0)

/**
 * @brief 	 解锁
 */
#define RB_UNLOCK(p_rb)       \
    do {                      \
        if ((p_rb)->unlock)   \
            (p_rb)->unlock(); \
    } while (0)

/**
 * @brief 	 将void指针转换为uint8_t指针
 */
#define U8_DATA(_p_void_data) ((uint8_t *)(_p_void_data))

/**
 * @fn RB_init
 * @brief 初始化环形缓冲区
 * @param rb [in] 环形缓冲区对象指针
 * @param elem_buffer [in] 环形缓冲区的元素缓存数组
 * @param elem_size [in] 每个元素的大小（单位: Byte）
 * @param elem_max [in] 元素缓存数组最多缓存的元素数量
 * @param lock [in] 加锁操作，用于数据存取的互斥操作
 * @param unlock [in] 解锁操作，用于数据存取的互斥操作
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_init(RingBuffer *rb, void *elem_buffer, uint32_t elem_size,
                          uint32_t elem_max, void (*lock)(void),
                          void (*unlock)(void))
{
    // 参数合法性判断
    if ((!rb) || (!elem_buffer) || (!elem_size) || (!elem_max)) {
        return RB_PARAMS_ERR;
    }

    if ((lock || unlock) && (!(lock && unlock))) {
        return RB_PARAMS_ERR;
    }

    // 环形缓冲区结构体元素赋值
    rb->elem_buffer = elem_buffer;
    rb->elem_size   = elem_size;
    rb->elem_max    = elem_max;

    RB_INDEX_CLEAR(rb->r);
    RB_INDEX_CLEAR(rb->w);

    // 默认情况下的配置：不使用DMA存放数据，允许覆盖数据，允许分割存放
    rb->config.is_dma_store      = 0;
    rb->config.allow_overwrite   = 1;
    rb->config.allow_sqlit_store = 1;
    CONFIG_LIMIT(rb->config);

    rb->lock   = lock;
    rb->unlock = unlock;

    return RB_OK;
}

/**
 * @fn RB_config
 * @brief 配置环形缓冲区
 * @param rb [in] 环形缓冲区对象指针
 * @param config [in] 环形缓冲区的配置结构体
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_config(RingBuffer *rb, RingBufferConfig *config)
{
    // 参数合法性判断
    if ((!rb) || (!config)) {
        return RB_PARAMS_ERR;
    }

    // 环形缓冲区的配置结构体元素赋值
    rb->config.is_dma_store      = config->is_dma_store;
    rb->config.allow_overwrite   = config->allow_overwrite;
    rb->config.allow_sqlit_store = config->allow_sqlit_store;
    CONFIG_LIMIT(rb->config);

    return RB_OK;
}

/**
 * @fn RB_push
 * @brief 往环形缓冲区中存放元素（数据）
 * @param rb [in] 环形缓冲区对象指针
 * @param elem [in] 需要存放的元素指针或者需要存放的元素数组
 * @param num [in] 需要存放的元素数量
 * @param start [out] 元素在缓冲区存放的位置索引头，用于给外部提供该数据的存放信息，不需要时传入NULL（0）
 * @param end [out] 元素在缓冲区存放的位置索引尾，用于给外部提供该数据的存放信息，不需要时传入NULL（0）
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_push(RingBuffer *rb, const void *elem, const uint32_t num,
                          uint32_t *start, uint32_t *end)
{
    // 参数合法性判断
    if ((!rb) || (!elem) || (!num)) {
        return RB_PARAMS_ERR;
    }

    // 数据存入时加锁
    RB_LOCK(rb);

    // 当不允许分割存放时，待存放的数据超过缓存数组存入索引到数组尾的长度（表示待存放数据需要分成2部分存放），
    // 需要将存入索引提到缓冲区头部开始存放数据
    if ((!rb->config.allow_sqlit_store) && (rb->w.index + num > rb->elem_max)) {
        rb->w.flag  = !rb->w.flag;
        rb->w.index = 0;
    }

    // 记录开始存放的索引
    if (start) {
        *start = rb->w.index;
    }

    // 存入数据
    for (uint32_t i = 0; i < num; i++) {
        if (!IS_FULL(rb)) {
            // 环形缓存未满，直接存入数据
            ELEM_COPY(U8_DATA(rb->elem_buffer) + rb->elem_size * rb->w.index,
                      U8_DATA(elem) + rb->elem_size * i, 
                      rb->elem_size);
        } else {
            // 环形缓存已满
            if (rb->config.allow_overwrite) {
                // 允许覆盖时，直接存入数据
                ELEM_COPY(U8_DATA(rb->elem_buffer) + rb->elem_size * rb->w.index,
                          U8_DATA(elem) + rb->elem_size * i, 
                          rb->elem_size);
            } else {
                // 不允许覆盖时，错误退出
                // 错误退出时解锁
                RB_UNLOCK(rb);
                return RB_OVERWRITE;
            }
        }

        // 每存入一个元素，存入索引+1，并每次判断是否需要处理到缓存数组尾的情况
        rb->w.index++;
        RB_INDEX_END(rb->w, rb->elem_max);
    }

    // 记录结束存放的索引
    if (end) {
        if (rb->w.index == 0)
            *end = rb->elem_max;
        else
            *end = rb->w.index;
    }

    // 数据存入后解锁
    RB_UNLOCK(rb);

    return RB_OK;
}

/**
 * @fn RB_push_dma
 * @brief 使用DMA往环形缓冲区中存放元素（数据）
 * @param rb [in] 环形缓冲区对象指针
 * @param pos [in] DMA存入数据后，数据尾索引
 * @param start [out] 元素在缓冲区存放的位置索引头，用于给外部提供该数据的存放信息，不需要时传入NULL（0）
 * @param end [out] 元素在缓冲区存放的位置索引尾，用于给外部提供该数据的存放信息，不需要时传入NULL（0）
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_push_dma(RingBuffer *rb, const uint32_t pos,
                              uint32_t *start, uint32_t *end)
{
    // 参数合法性判断
    if (!rb) {
        return RB_PARAMS_ERR;
    }

    // 数据存入时加锁
    RB_LOCK(rb);

    // 记录开始存放的索引
    if (start) {
        *start = rb->w.index;
    }

    // dma写入尾索引小于当前写索引，说明dma循环到头部了，需要翻转flag
    if (pos < rb->w.index) {
        rb->w.flag = !rb->w.flag;
    }

    rb->w.index = pos;
    RB_INDEX_END(rb->w, rb->elem_max);

    // 记录结束存放的索引
    if (end) {
        if (rb->w.index == 0)
            *end = rb->elem_max;
        else
            *end = rb->w.index;
    }

    // 当前写索引超过了读索引，数据覆盖了，读索引重新赋值，和写索引一样了
    if ((rb->w.flag != rb->r.flag) && (rb->w.index > rb->r.index)) {
        rb->r.index = rb->w.index;
        // 错误退出时解锁
        RB_UNLOCK(rb);
        return RB_OVERWRITE;
    }

    // 数据存入后解锁
    RB_UNLOCK(rb);
    return RB_OK;
}

/**
 * @fn RB_pop
 * @brief 从环形缓冲区取出num数量的元素，取出后释放了相应的元素
 * @param rb [in] 环形缓冲区对象指针
 * @param num [in] 需要取出的元素数量
 * @param elem [out] 取出元素存放缓冲区，必须提供有效的指针
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_pop(RingBuffer *rb, const uint32_t num, void *elem)
{
    // 参数合法性判断
    if ((!rb) || (!num) || (!elem)) {
        return RB_PARAMS_ERR;
    }

    // 环形缓冲区为空，取不出数据
    if (IS_EMTRY(rb)) {
        return RB_EMPTY;
    }

    // 取数据前加锁
    RB_LOCK(rb);

    // 将元素一个一个复制出来
    for (uint32_t i = 0; i < num; i++) {
        ELEM_COPY(U8_DATA(elem) + rb->elem_size * i,
                  U8_DATA(rb->elem_buffer) + rb->elem_size * rb->r.index, 
                  rb->elem_size);
        rb->r.index++;
        RB_INDEX_END(rb->r, rb->elem_max);
    }

    // 取数据后解锁
    RB_UNLOCK(rb);

    return RB_OK;
}

/**
 * @fn RB_look
 * @brief 查看环形缓冲区的num个元素，不会释放元素，仅仅查看
 * @param rb [in] 环形缓冲区对象指针
 * @param num [in] 需要查看的元素数量
 * @param elem [out] 查看元素存放缓冲区，必须提供有效的指针
 * @return RingBufferErrType 错误类型
 */
RingBufferErrType RB_look(RingBuffer *rb, const uint32_t num, void *elem)
{
    uint32_t tmp = 0;

    // 参数合法性判断
    if ((!rb) || (!num) || (!elem)) {
        return RB_PARAMS_ERR;
    }

    // 环形缓冲区为空，没有数据查看
    if (IS_EMTRY(rb)) {
        return RB_EMPTY;
    }

    // 查看数据前加锁
    RB_LOCK(rb);

    // 因为只是查看，不能动缓冲区的索引
    tmp = rb->r.index;
    for (uint32_t i = 0; i < num; i++) {
        ELEM_COPY(U8_DATA(elem) + rb->elem_size * i, 
                  U8_DATA(rb->elem_buffer) + rb->elem_size * tmp,
                  rb->elem_size);
        tmp++;
        if (tmp == rb->elem_max)
            tmp = 0;
    }

    // 查看数据后解锁
    RB_UNLOCK(rb);

    return RB_OK;
}
